package com.learning.bio.server;

import com.learning.bio.utils.BaseUtils;
import com.learning.bio.utils.LambdaExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

/**
 * @Author: wcl
 * @Description:
 * @Date: Create in 2:53 PM 2018/11/1
 * @Modified By:
 */
public class Server {

    private static final ExecutorService threadPool = BaseUtils.getThreadPool();

    private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
    /**
     * 服务端默认端口
     */
    private static final int DEFAULT_PORT = 9999;

    private static ServerSocket serverSocket;

    private static Thread thread;

    private static final Object startLock = new Object();

    private static final Object closeLock = new Object();

    private static final ConcurrentHashMap<String, Socket> clients = new ConcurrentHashMap();

    public static void main(String[] args) {

        threadPool.submit(() -> {
            try {
                LOGGER.info("{}->开启bio服务端。。。", Thread.currentThread().getName());
                start(DEFAULT_PORT);
            } catch (IOException e) {
                if("Socket closed".equals(e.getMessage())) {
                    LOGGER.info("服务端关闭");
                } else {
                    LOGGER.error("启动服务端出错->{}", e.getMessage());
                }

            }
        });
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {

            LocalDateTime now = LocalDateTime.now();
            LOGGER.info("开始执行结束程序:{}", now);
            Server.stop();
            threadPool.shutdownNow();
        }));
    }

    public static void start(int port) throws IOException {
        synchronized (startLock) {

            if(serverSocket != null) {
                return;
            }

            serverSocket = new ServerSocket(port);
            thread = Thread.currentThread();
            while (true) {

                Socket accept = serverSocket.accept();

                String clientInfo = accept.getInetAddress() + ":" + accept.getPort();
                if(!clients.contains(clientInfo)) {
                    clients.put(clientInfo, accept);
                }

                threadPool.submit(() -> {
                    LOGGER.info(accept.getPort()  + "->" + accept);
                    BufferedReader is = null;
                    try {
                        is = new BufferedReader(new InputStreamReader(accept.getInputStream()));
                        String content;
                        ThreadLocal<String> threadLocal = new ThreadLocal<>();
                        while (true) {
                            if ((content = is.readLine()) != null) {
                                LOGGER.info("客户端输入:{}", content);
                                threadLocal.set(content);
                                clients.entrySet().stream()
                                        .filter(set -> !clientInfo.equals(set.getKey()))
                                        .map(LambdaExceptionUtil.rethrowFunction(set -> new PrintStream(set.getValue().getOutputStream(), true)))
                                        .forEach(out -> out.println(clientInfo + "发送" + threadLocal.get()));
                            }
                        }
                    } catch (IOException e) {
                        LOGGER.error("服务端io异常:{}", e.getMessage());
                    } finally {
                        LOGGER.info("关闭流");
                        BaseUtils.close(is, "bio服务端输入流");
//                        BaseUtils.close(out, "bio服务端输出流");
                    }
                });
            }
        }
    }



    public static void stop() {
        synchronized (closeLock) {
            LOGGER.info("服务端准备关闭");
            BaseUtils.close(serverSocket, "服务");
            if(thread != null) {
                thread.interrupt();
            }

            LOGGER.info("服务端正常关闭:{}", serverSocket);
        }
    }

}
